package pl.wr.math.obiect;

import java.util.Arrays;

import pl.wr.math.Component;
import pl.wr.math.Operation;

/**
 * Class used to perform basic operations on matrices: matrix addition,
 * subtraction, multiplication, exponentiation, transposition, determination of
 * trace matrix, determinant of the matrix and its inverse. It also has methods
 * to determine the type of the matrix: zero, square, diagonal, unit,
 * symmetrical, diagonal of symmetrical, binary, upper triangular, lower
 * triangular, triangular, singular, nonsingular, scalar, column vector, row
 * vector or anti-diagonal. <BR>
 * Code developed on the basis http://www.darmoweskrypty.linuxpl.info
 * 
 * @author wieslaw.rodak
 * 
 */
public class Matrix implements Component {

    /**
     * Table of matrix [rows][columns]
     */
    private double[][] matrixTable;

    /**
     * Make new matrix object
     * 
     * @param matrixTable
     *            double[][] - [rows][columns]
     */
    public Matrix(double[][] matrixTable) {
        this.matrixTable = Arrays.copyOf(matrixTable, matrixTable.length);
    }

    /**
     * The method of calculating the determinant for the given matrix. When the
     * matrixTable is an array of 1x1 - determinant is equal to the element
     * array [0][0], if is an array of 2x2 - determinant is calculated from the
     * formula:
     * <code>array[0][0] * array[1][1] - array[0][1] * array[1][0]</code>, when
     * the specified matrixTable is not square ArithmeticException exception is
     * returned.
     * 
     * @param matrix
     *            Matrix for which we set the determinant
     * @return determinant of matrix
     * @throws ArithmeticException
     *             if the matrix is not square
     */
    public static double getDeterminant(final Matrix matrix) {
        final double[][] matrixTable = matrix.getMatrixTable();
        final int rowLength = matrixTable.length;
        final int columnLength = matrixTable[0].length;
        double determinant = 0;

        if (rowLength != columnLength) {
            throw new ArithmeticException("You can not determine that the determinant of the matrix is not square");
        } else if (rowLength == 1) {
            determinant = matrixTable[0][0];
        } else if (rowLength == 2) {
            determinant = matrixTable[0][0] * matrixTable[1][1] - matrixTable[0][1] * matrixTable[1][0];
        } else {
            double[][] tempTable = new double[rowLength + (rowLength - 1)][columnLength];
            for (int row = 0, tempRow = 0; row < tempTable.length; row++, tempRow++) {
                for (int col = 0; col < columnLength; col++) {
                    if (tempRow < rowLength && col < columnLength) {
                        // next
                    } else {
                        tempRow = 0;
                    }
                    tempTable[row][col] = matrixTable[tempRow][col];
                }
            }

            double tempDeterminant = 1;
            int tempRow;

            for (int row = 0; row < rowLength; row++) {
                tempRow = row;
                for (int col = 0; col < columnLength; col++) {
                    tempDeterminant *= tempTable[tempRow][col];
                    tempRow++;
                }
                determinant += tempDeterminant;
                tempDeterminant = 1;
            }

            tempDeterminant = 1;
            for (int row = 0; row < rowLength; row++) {
                tempRow = row;
                for (int col = columnLength - 1; col >= 0; col--) {
                    tempDeterminant *= tempTable[tempRow][col];
                    tempRow++;
                }
                determinant -= tempDeterminant;
                tempDeterminant = 1;
            }
        }
        return determinant;
    }

    /**
     * The method of calculating the determinant of this instance.
     * 
     * @return determinant of matrix
     * @see #getDeterminant(Matrix)
     */
    public double getDeterminant() {
        return getDeterminant(this);
    }

    /**
     * The method add matrix to this instance.
     * 
     * @param matrix
     *            - matrix to add.
     * @see #add(Matrix, Matrix)
     */
    public void add(final Matrix matrix) {
        this.matrixTable = add(matrix, this).getMatrixTable();
    }

    /**
     * The method of adding the two matrices. Matrices are added together only
     * if they have the same dimensions, ie .: they have the same the number of
     * columns and rows, otherwise thrown ArithmeticException.
     * 
     * @param matrix1
     * @param matrix2
     * @return Matrix contains sum of input matrixes
     */
    public static Matrix add(final Matrix matrix1, final Matrix matrix2) {
        return add_sub(matrix1, matrix2, Operation.add);
    }

    /**
     * The method subtract matrix from this instance.
     * 
     * @param matrix
     *            - matrix to subtract.
     */
    public void subtract(final Matrix matrix) {
        this.matrixTable = subtract(this, matrix).getMatrixTable();

    }

    /**
     * The method of subtracting the two matrices. Matrices are substracted
     * together only if they have the same dimensions, ie .: they have the same
     * the number of columns and rows, otherwise thrown ArithmeticException.
     * 
     * @param matrix1
     * @param matrix2
     * @return Matrix contains difference of input matrixes.
     */
    public static Matrix subtract(final Matrix matrix1, final Matrix matrix2) {
        return add_sub(matrix1, matrix2, Operation.sub);
    }

    /**
     * @param matrix1
     * @param matrix2
     * @param operation
     * @return
     */
    private static Matrix add_sub(final Matrix matrix1, final Matrix matrix2, final Operation operation) {
        final double[][] table1 = matrix1.getMatrixTable();
        final double[][] table2 = matrix2.getMatrixTable();
        final int table1Rows = table1.length;
        final int table1Cols = table1[0].length;
        final int table2Rows = table2.length;
        final int table2Cols = table2[0].length;
        double[][] returnTable = null;

        if (table1Rows == table2Rows && table1Cols == table2Cols) {
            returnTable = new double[table1Rows][table1Cols];
            for (int row = 0; row < table1Rows; row++) {
                for (int col = 0; col < table1Cols; col++) {
                    switch (operation) {
                    case add:
                        returnTable[row][col] = (table1[row][col] + table2[row][col]);
                        break;
                    case sub:
                        returnTable[row][col] = (table1[row][col] - table2[row][col]);
                        break;
                    }
                }
            }
        } else {
            throw new ArithmeticException("You can not subtract / add from one another matrix of different sizes");
        }
        return new Matrix(returnTable);
    }

    /**
     * Metoda służąca do pomnożenia macierzy przez skalar.
     * 
     * @param scalar
     *            liczba przez którą wymnażana jest macierz
     * @see Matrix#multiplyByScalar(double, Matrix)
     */
    public void multiplyByScalar(final double scalar) {
        this.matrixTable = multiplyByScalar(scalar, this).getMatrixTable();
    }

    /**
     * Prywatna metoda służąca do pomnożenia podanej tablicy przez skalar.
     * 
     * @param scalar
     *            liczba przez która mnożona jest tablica
     * @param table
     *            przez którą mnożony jest skalar
     * @return wymnożona tablica
     */
    public static Matrix multiplyByScalar(final double scalar, final Matrix matrix) {
        final double[][] table = matrix.getMatrixTable();
        final double[][] returnTable = new double[table.length][table[0].length];
        for (int row = 0; row < table.length; row++) {
            for (int col = 0; col < table[0].length; col++) {
                returnTable[row][col] = (table[row][col] * scalar);
            }
        }
        return new Matrix(returnTable);
    }

    /**
     * Metoda zwracająca macierz w postaci tablicy wielowymiarowej.
     * 
     * @return macierz w formie tablicy wielowymiarowej
     */
    public double[][] getMatrixTable() {
        return matrixTable;
    }

    /**
     * Sprawdza czy macierz jest macierzą zerową, czyli taka która składa się
     * tylko z samych zer.
     * 
     * @return true jeśli tablica jest zerowa, false jeśli nie
     */
    public boolean isZeroMatrix() {
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (this.matrixTable[row][col] != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Metoda sprawdzająca czy dana macierz jest macierzą kwadratową czyli taką
     * która ma tyle samo kolumn co wierszy.
     * 
     * @return boolean True jest kwadratowa, false jeśli nie jest kwadratowa
     */
    public boolean isSquareMatrix() {
        return this.matrixTable.length == this.matrixTable[0].length;
    }

    /**
     * Metoda służąca do pomnożenia aktualnej macierzy przez macierz podana jako
     * parametr metody.
     * 
     * @param matrix
     *            przez która mnożymy aktualną macierz
     */
    public void multiply(final Matrix matrix) {
        this.matrixTable = multiply(matrix, this).getMatrixTable();
    }

    public static Matrix multiply(final Matrix matrix1, final Matrix matrix2) {
        final double[][] table1 = matrix1.getMatrixTable();
        final double[][] table2 = matrix2.getMatrixTable();
        final double[][] returnTable = multiplyTables(table1, table2);
        return new Matrix(returnTable);
    }

    /**
     * Sprawdza czy podana tablica jest diagonalna, czyli taka w której
     * wszystkie elementy leżące poza główna przekątna są zerami.
     * 
     * @return true jeśli tablica jest diagonalna, false jeśli nie
     */
    public boolean isDiagonal() {
        if (!this.isSquareMatrix()) {
            return false;
        }
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (row != col && this.matrixTable[row][col] != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Metoda sprawdzająca czy dana macierz jest jednostkowa czyli taka w której
     * elementy leżące na głównej przekątnej sa jedynkami a pozostałe elementy
     * są zerami.
     * 
     * @return true jeśli tablica jest jednostkowa, false jeśli nie jest
     */
    public boolean isUnitary() {
        if (!this.isDiagonal()) {
            return false;// tablice nie diagonalne nie mogą być jednostkowe
        }
        for (int row = 0, col = 0; row < this.matrixTable.length; row++, col++) {
            if (this.matrixTable[row][col] != 1) {
                return false;
            }
        }
        return true;
    }

    /**
     * Sprawdza czy macierz jest symetryczna to znaczy taka dla której elementy
     * leżące na pozycjach [i][j] i [j][i] są sobie równe.
     * 
     * @return true jeśli tablica jest symetryczna, false jeśli nie
     */
    public boolean isSymmetrical() {
        if (!this.isSquareMatrix()) {
            return false; // tablica nie kwadratowe nie mogą być symetryczne
        } else {
            for (int row = 0; row < this.matrixTable.length; row++) {
                for (int col = 0; col < this.matrixTable[0].length; col++) {
                    // if (this.matrixTable[i][j] != this.matrixTable[j][i]) {
                    if (Double.compare(this.matrixTable[row][col], this.matrixTable[col][row]) != 0) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * Sprawdza czy tablica jest skośnosymetryczna to znaczy że dla każdego
     * elementu [i][j]=-[j][i]
     * 
     * @return true jeśli tablica jest skośnosymetryczna false jeśli nie jest
     */
    public boolean isDiagonallySymmetrical() {
        if (!this.isSquareMatrix()) {
            return false; // tablice nie kwadratowe nie mogą być
                          // skosnosymetryczne
        } else {
            for (int row = 0; row < this.matrixTable.length; row++) {
                for (int col = 0; col < this.matrixTable[0].length; col++) {
                    // if (this.matrixTable[i][j] != (-this.matrixTable[j][i]))
                    // {
                    if (Double.compare(this.matrixTable[row][col], (-this.matrixTable[col][row])) != 0) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * Metoda sprawdzająca czy podana macierz jest macierzą binarna czyli taką
     * której elementy są albo jedynkami albo zerami.
     * 
     * @return true jeśli macierz jest binarna, false jeśli nie jest
     */
    public boolean isBinary() {
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (this.matrixTable[row][col] == 0 || this.matrixTable[row][col] == 1) {
                } else {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Metoda przekształcająca macierz na typ String, do wygenerowanego stringa
     * dodawane są dodatkowe formatowania aby macierz była czytelniejsza.
     * 
     * @return tablica w formie obiektu String
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int row = 0; row < this.matrixTable.length; row++) {
            sb.append('|');
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                sb.append(' ');
                sb.append(this.matrixTable[row][col]);
            }
            sb.append('|');
            sb.append(System.getProperty("line.separator"));

        }
        return sb.toString();
    }

    /**
     * Metoda służąca do transponowania macierzy.
     * 
     * @see #transpose(double[][])
     */
    public void transpose() {
        this.matrixTable = transpose(this).getMatrixTable();
    }

    /**
     * Prywatna metoda służąca do zamieniania pozycjami wierszy tablicy z jej
     * kolumnami.
     * 
     * @param tablica
     *            do transpozycji
     * @return tablica z zamienionymi kolumnami i wierszami
     * @see #transpose()
     */
    public static Matrix transpose(final Matrix matrix) {
        final double[][] table = matrix.getMatrixTable();
        final double[][] returnTable = new double[table[0].length][table.length];
        for (int row = 0; row < table.length; row++) {
            for (int col = 0; col < table[0].length; col++) {
                returnTable[col][row] = table[row][col];
            }
        }
        return new Matrix(returnTable);
    }

    /**
     * Metoda sprawdzająca czy dana macierz jest górnotrójkątna czyli taka
     * macierz kwadratowa w której wszystkie elementy poniżej głównej przekątnej
     * są zerami.
     * 
     * @return true jeśli macierz jest górnotrójkątna, false jeśli nie jest
     */
    public boolean isUpperTriangular() {
        if (!this.isSquareMatrix()) {
            return false;
        }
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (row > col && this.matrixTable[row][col] != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Metoda sprawdzająca czy dana macierz jest dolnotrójkątna czyli taka
     * macierz kwadratowa w której wszystkie elementy powyżej głównej przekątnej
     * są zerami.
     * 
     * @return true jeśli macierz jest dolnotrójkątna, false jeśli nie jest
     */
    public boolean isLowerTriangular() {
        if (!this.isSquareMatrix()) {
            return false;
        }
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (col > row && this.matrixTable[row][col] != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Sprawdza czy dana macierz jest trójkątna czyli taka dla której elementy
     * leżące poniżej lub powyżej głównej przekątnej są zerami.
     * 
     * @return true jeśli macierz jest trójkątna, false jeśli nie jest
     */
    public boolean isTriangular() {
        if (!this.isLowerTriangular() && !this.isUpperTriangular()) {
            return false;
        }
        return true;
    }

    /**
     * Sprawdza czy dana macierz jest osobliwa czyli taka dla której wyznacznik
     * jest równy 0.
     * 
     * @return true jeśli macierz jest osobliwa, false jeśli nie jest
     */
    public boolean isSingluar() {
        if (getDeterminant() != 0) {
            return false;
        }
        return true;
    }

    /**
     * Sprawdza czy dana macierz jest nieosobliwa czyli taka dla której
     * wyznacznik jest różny od 0.
     * 
     * @return true jeśli macierz jest nieosobliwa, false jeśli nie jest
     */
    public boolean isNonSingluar() {
        if (this.getDeterminant() == 0) {
            return false;
        }
        return true;
    }

    /**
     * Metodą sprawdzająca czy dana macierz jest skalarna czyli taka macierz
     * która jest macierzą diagonalną i równocześnie wszystkie jej elementy
     * leżące na głównej przekątnej są sobie równe.
     * 
     * @return true jeśli macierz jest skalarna, false jeśli nie jest
     * @see #isDiagonal()
     */
    public boolean isSkalar() {
        if (!this.isDiagonal()) {
            return false;
        }
        final double element = this.matrixTable[0][0];
        for (int row = 0, col = 0; row < this.matrixTable.length; row++, col++) {
            // if (this.matrixTable[i][j] != element) {
            if (Double.compare(this.matrixTable[row][col], element) != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * Sprawdza czy dana macierz jest idempotentna czyli taką macierz kwadratowa
     * dla której jej kwadrat jest równy jej samej A=A^2.
     * 
     * @return true jeśli macierz jest idempotentna, false jeśli nie jest
     */
    public boolean isIdempotent() {
        if (!this.isSquareMatrix()) {
            return false;
        }
        final double[][] squareTable = new double[this.matrixTable.length][this.matrixTable[0].length];
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                double temp = 0;
                for (int w = 0; w < this.matrixTable.length; w++) {
                    temp += this.matrixTable[row][w] * this.matrixTable[w][col];
                }
                squareTable[row][col] = temp;
            }
        }
        // Porównywanie macierzy A i A^2
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                // if (this.matrixTable[i][j] != squareTable[i][j]) {
                if (Double.compare(this.matrixTable[row][col], squareTable[row][col]) != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Prywatna metoda jej zadaniem jest podnoszenie do potęgi podanej tablicy,
     * metoda wykorzystuje przy potęgowaniu algorytm szybkiego potęgowania.
     * 
     * @param table
     *            tablica która jest potęgowana
     * @param power
     *            wartość do której podnoszona jest tablica
     * @return tablica podniesiona do podanej potęgi
     * @see #toPower(int)
     */
    private static double[][] tableToPower(final double[][] table, final int power) {
        if (power == 0) {
            final double[][] temp = new double[table.length][table[0].length];
            for (int row = 0; row < temp.length; row++) {
                for (int col = 0; col < temp[0].length; col++) {
                    if (row == col) {
                        temp[row][col] = 1;
                    } else {
                        temp[row][col] = 0;
                    }
                }
            }
            return temp;
        } else if (power % 2 != 0) {
            return multiplyTables(table, tableToPower(table, power - 1));
        } else {
            final double[][] temp = tableToPower(table, power >> 1);
            return multiplyTables(temp, temp);
        }
    }

    /**
     * Metoda służąca do podnoszenia macierzy do podanej potęgi, w przypadku gdy
     * macierz nie jest kwadratowa lub podany wykładnik jest ujemny wyrzucany
     * jest wyjątek RuntimeException.
     * 
     * @param wykladnik
     *            wartość potęgi do jakiej podnoszona jest macierz
     * @exception RuntimeException
     *                macierz nie jest kwadratowa lub wykładnik ujemny
     * @see #isSquareMatrix()
     */
    public void toPower(final int power) {
        this.matrixTable = toPower(this, power).getMatrixTable();
    }

    public static Matrix toPower(final Matrix matrix, final int power) {
        double[][] table = matrix.getMatrixTable();
        if (!matrix.isSquareMatrix()) {
            throw new ArithmeticException("Nie można potęgować macierzy które nie są kwadratowe");
        } else {
            if (power >= 2) {
                if (power % 2 == 0) {
                    table = tableToPower(table, power);
                }
            } else if (power == 1) {// A^1=A
            } else if (power == 0) {// tablica jednostkowa A^0=I
                for (int row = 0; row < table.length; row++) {
                    for (int col = 0; col < table[0].length; col++) {
                        table[row][col] = 1;
                    }
                }
            } else {
                throw new ArithmeticException("Wykładnik nie może być ujemny");
            }
        }
        return new Matrix(table);
    }

    /**
     * Metoda służąca do pomnożenia dwóch tablic: tab1 i tab2, tablice te można
     * pomnożyć przez siebie pod warunkiem ze ilość kolumn tablicy tab1 jest
     * taka sama jak ilość wierszy tablicy tab2, w przeciwnym wypadku wyrzucany
     * jest wyjątek RuntimeException.
     * 
     * @param table1
     *            pierwsza tablica
     * @param table2
     *            druga tablica
     * @return tablica będąca iloczynem tablic tab1 i tab2
     */
    private static double[][] multiplyTables(final double[][] table1, final double[][] table2) {
        final double[][] multipledTable = new double[table1.length][table2[0].length];
        if (table1[0].length == table2.length) {
            for (int row1 = 0; row1 < table1.length; row1++) {// ilosc wierszy
                                                              // tab1
                for (int col2 = 0; col2 < table2[0].length; col2++) { // ilosc
                                                                      // kolumn
                                                                      // tab2
                    double temp = 0;
                    for (int row2 = 0; row2 < table2.length; row2++) { // ilosc
                                                                       // wierszy
                        // tab2
                        temp += table1[row1][row2] * table2[row2][col2];
                    }
                    multipledTable[row1][col2] = temp;
                }
            }
        } else {
            throw new ArithmeticException("Podane tablice mają niewłasciwe wymiary");
        }
        return multipledTable;
    }

    /**
     * Sprawdza czy macierz jest kolumnowa czyli taka która posiada jedną
     * kolumnę.
     * 
     * @return true dla macierzy kolumnowej, false dla macierzy nie kolumnowej
     */
    public boolean isColumnVector() {
        return this.matrixTable[0].length == 0;
    }

    /**
     * Sprawdza czy tablica jest wierszowa czyli taka która posiada jeden
     * wiersz.
     * 
     * @return boolean true jeśli jest wierszowa, false jeśli nie jest
     */
    public boolean isRowVector() {
        return this.matrixTable.length == 0;
    }

    /**
     * Metoda wyliczająca ślad macierzy czyli sumę elementów na głównej
     * przekątnej macierzy, macierz dla której wyznaczany jest ślad musi być
     * macierzą kwadratowa.
     * 
     * @return ślad macierzy
     */
    public double getTraceMatrix() {
        double sum = 0;
        if (!this.isSquareMatrix()) {
            throw new ArithmeticException("Nie można wyznaczyć śladu dla macierzy nie kwadratowej");
        } else {
            for (int row = 0; row < this.matrixTable.length; row++) {
                sum += this.matrixTable[row][row];
            }
        }
        return sum;
    }

    /**
     * Metoda sprawdzająca czy dana macierz jest antydiagonalna, czyli taka
     * która zawiera same zera oprócz elementów na przekątnej biegnącej od
     * prawego górnego wierzchołka do lewego dolnego. Macierz może być
     * antydiagonalna pod warunkiem ze jest kwadratowa.
     * 
     * @return true jesli jest antydiagonalna, false jesli nie jest
     *         antydiagonalna
     */
    public boolean isAntidiagonal() {
        if (!this.isSquareMatrix()) {
            return false;
        }
        int temp = this.matrixTable[0].length - 1;
        for (int row = 0; row < this.matrixTable.length; row++) {
            for (int col = 0; col < this.matrixTable[0].length; col++) {
                if (col != temp && this.matrixTable[row][col] != 0) {
                    return false;
                }
            }
            temp--;
        }
        return true;
    }

    /**
     * Metoda służąca do wyznaczania macierzy odwrotnej do podanej w
     * konstruktorze klasy tablicy. W pierwszej kolejności sprawdzane jest czy
     * dana tablica jest kwadratowa, tylko dla takich tablic można wyznaczyć
     * macierz odwrotna, jeśli nie jest kwadratowa wyrzucany jest wyjątek
     * RuntimeException. Dla macierzy kwadratowych składających się tylko z
     * jednego elementu macierz odwrotna jest równa 1/(wyznacznik tablicy), dla
     * macierzy o większej ilości elementów wyznaczana jest macierz dołączona a
     * samą macierz odwrotna wyznacza się jako 1/(wyznacznik tablicy)*(macierz
     * dołączona).
     * 
     * @return macierz odwrotna
     */
    public Matrix inverseMatrix() {
        return inverseMatrix(this);
    }

    public Matrix inverseMatrix(final Matrix matrix) {
        if (!this.isSquareMatrix()) {
            throw new ArithmeticException("You can not determine the inverse matrix for the matrix is not square");
        } else {
            final double[][] matrixTable = matrix.getMatrixTable();
            final int rowLength = matrixTable.length;
            final int columnLength = matrixTable[0].length;
            double[][] revertedTable;

            if (rowLength == 1) {
                revertedTable = new double[1][1];
                revertedTable[0][0] = 1 / this.getDeterminant();
                return new Matrix(revertedTable);
            } else {
                revertedTable = new double[rowLength][columnLength];
                final double[][] addedTable = new double[rowLength][columnLength];
                for (int row = 0; row < rowLength; row++) {
                    for (int col = 0; col < columnLength; col++) {
                        final double[][] tempTable = new double[rowLength - 1][columnLength - 1];
                        int tempRow = 0, tempCol = 0;
                        for (int row2 = 0; row2 < rowLength; row2++) {
                            for (int col2 = 0; col2 < columnLength; col2++) {
                                if (row2 != row && col2 != col) {
                                    if (tempCol >= tempTable.length) {
                                        tempCol = 0;
                                        tempRow++;
                                    }
                                    tempTable[tempRow][tempCol] = this.matrixTable[row2][col2];
                                    tempCol++;
                                }
                            }
                        }

                        double tempDeterminat = getDeterminant(new Matrix(tempTable));

                        if ((row + col) % 2 != 0) {// Niparzyste czyli zmiana
                                                   // znaku
                            // wyznacznika
                            if (tempDeterminat > 0) {
                                tempDeterminat -= 2 * tempDeterminat;
                            } else {
                                tempDeterminat -= 2 * tempDeterminat;
                            }
                        }
                        addedTable[row][col] = tempDeterminat;
                    }
                }
                return multiplyByScalar(1 / this.getDeterminant(), transpose(new Matrix(addedTable)));
            }
        }
    }

}
